from __future__ import absolute_import

import importlib
import os
import sys

import antlr4
import pyftype

from xscanner import modules
from xscanner.grammar import RuleLexer, RuleParser, visitor


class ScanResult(object):
    def __init__(self):
        self.__names = []
        self.__main_result = None
        self.__path = None
        self.__sub_results = []

    @property
    def path(self):
        return self.__path

    @path.setter
    def path(self, value):
        self.__path = value

    @property
    def main(self):
        return self.__main_result

    @main.setter
    def main(self, value: visitor.Result):
        self.__main_result = value

        for item in self.__main_result:
            self.__names.append(item.name)

    @property
    def names(self):
        return self.__names

    @property
    def subs(self):
        return self.__sub_results

    def add_sub(self, result):
        self.__sub_results.append(result)

    def __str__(self):
        return self.__path + "." + str(self.__names)


class Scanner:
    def __init__(self, path):
        self.path = path  # 规则路径
        self._init_rules()
        self._init_modules()

    def _init_rules(self):
        if not os.path.exists(self.path):
            raise FileNotFoundError()

        # TODO 全部读取出来，组合成一个大的字符串
        content = ""
        if os.path.isdir(self.path):
            for path in os.listdir(self.path):
                print(path)
        else:
            with open(self.path) as f:
                content = f.read()

        lex = RuleLexer.RuleLexer(antlr4.InputStream(content))
        parser = RuleParser.RuleParser(antlr4.CommonTokenStream(lex))

        # TODO parser.removeErrorListeners()

        self.rules = parser.rules()

    def _init_modules(self):
        self._modules = {}

        modules_path = os.path.join(os.path.dirname(__file__), "modules")
        for root, dirs, files in os.walk(modules_path):
            for f in files:
                if "__" in f:
                    continue
                if f.endswith(".pyc"):
                    continue

                name = os.path.basename(f[:-3])
                location = os.path.join(root, f)
                spec = importlib.util.spec_from_file_location(name, location)
                module = importlib.util.module_from_spec(spec)
                sys.modules[name] = module
                spec.loader.exec_module(module)

                for item in dir(module):
                    if "_" in item:
                        continue
                    t = getattr(module, item)
                    if not hasattr(t, "__bases__"):
                        continue
                    if modules.Data not in t.__bases__:
                        continue
                    self._modules[t()._mime] = t

    def scan(self, path):
        kind = pyftype.guess(path)

        scan_result = ScanResult()
        if kind.MIME in self._modules:
            d = self._modules[kind.MIME]()
            d.from_path(path)
            scan_result = self.scan_data(d)
            d.close()

            if hasattr(d, "get_sub_files"):
                for k, v in d.get_sub_files().items():
                    self.__scan_data(os.path.join(path, k), v, scan_result)

        else:
            d = modules.Data()
            d.from_path(path)
            scan_result = self.scan_data(d)
            d.close()

        return scan_result

    def __scan_data(self, path, _bytes, scan_result):
        kind = pyftype.guess(_bytes)

        if kind.MIME in self._modules:
            d = self._modules[kind.MIME]()
            d.from_bytes(_bytes)
            d.set_path(path)
            scan_result.add_sub(self.scan_data(d))
            d.close()

            if hasattr(d, "get_sub_files"):
                for k, v in d.get_sub_files().items():
                    p = os.path.join(path, k)
                    self.__scan_data(p, v, scan_result)

        else:
            d = modules.Data()
            d.from_bytes(_bytes)
            d.set_path(path)
            scan_result.add_sub(self.scan_data(d))
            d.close()

    def scan_data(self, data: modules.Data):
        v = visitor.VisitorImp(data)
        v.visit(self.rules)

        sr = ScanResult()
        sr.path = data.get_path()
        sr.main = v.get_result()
        return sr

    def scan_mem(self, bytes):
        pass
